#ifndef __PORTAL_H__
#define __PORTAL_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sqlite3.h>
#include "list.h"

#define MAC_ADDR_LEN  6
#define CHALLENGE_LEN 16
#define SESSIONID_LEN CHALLENGE_LEN
#define USERNAME_LEN  254
#define PASSWORD_LEN  17
#define SSID_LEN      33
#define ACNAME_LEN    33
#define ERR_ID_LEN    64

#define TRUE 1
#define FALSE 0
#define MAX_EPOLL_EVENT  1024
#define GET "GET"
#define HEAD "HEAD"
#define POST "POST"
#define USER_AGENT "User-Agent:"
#define CRLFCRLF "\r\n\r\n"
#define AUTHEN "index.html"
#define LOGIN "login.html"
#define ONLINE "online.html"
#define OFFLINE "offline.html"
#define LOGOUT "logout.html"
#define PORTAL_DB "portal.db"

#define RX_BUF_SIZE 2048
#define TX_BUF_SIZE 4096
#define FD_POLL_SIZE 1024
#define PORTAL_KEY_LEN 256
#define DATABASE_FILE  64

#define PORTAL_DEBUG(fmt, ...) \
    if (portal_debug) \
      printf("%s %d %s: " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef uint bool;
typedef uint IPADDRTYPE;
typedef struct in6_addr IPADDRTYPE6;

enum http_method_e {
    HTTP_GET,
    HTTP_POST,
    HTTP_HEAD,
    HTTP_UNKNOW
};

enum portal_attr_e {
    PORTAL_ATTR_USERNAME = 0x01,
    PORTAL_ATTR_PASSWORD = 0x02,
    PORTAL_ATTR_CHALLENGE = 0x03,
    PORTAL_ATTR_CHAPPASSWORD = 0x04,
    PORTAL_ATTR_ERR_ID = 0x05,
    PORTAL_ATTR_SESSIONID = 0x0b,
    PORTAL_ATTR_BASIP = 0x0a,
    PORTAL_ATTR_NASID = 0x30,
    PORTAL_ATTR_SESSION_START_TIME = 0x31,
    PORTAL_ATTR_SESSION_STOP_TIME = 0x32,
    PORTAL_ATTR_SESSION_TIME = 0x33,
    PORTAL_ATTR_USER_AGENT = 0x34,
    PORTAL_ATTR_INPUT_OCTETS = 0x35,
    PORTAL_ATTR_OUTPUT_OCTETS = 0x36,
    PORTAL_ATTR_INPUT_PACKETS = 0x37,
    PORTAL_ATTR_OUTPUT_PACKETS = 0x38,
    PORTAL_ATTR_INPUT_GIGAWORDS = 0x39,
    PORTAL_ATTR_OUTPUT_GIGAWORDS = 0x3a,
    PORTAL_ATTR_USER_IPV6 = 0xf1
};

enum portal_type_e {
    PORTAL_TYPE_REQ_CHALLENGE = 1,
    PORTAL_TYPE_ACK_CHALLENGE,
    PORTAL_TYPE_REQ_AUTH,
    PORTAL_TYPE_ACK_AUTH,
    PORTAL_TYPE_REQ_LOGOUT,
    PORTAL_TYPE_ACK_LOGOUT,
    PORTAL_TYPE_AFF_ACK_AUTH,
    PORTAL_TYPE_NTF_LOGOUT,
    PORTAL_TYPE_REQ_INFO,
    PORTAL_TYPE_ACK_INFO,
    PORTAL_TYPE_MAC_INFO_REQ = 0x30,
    PORTAL_TYPE_MAC_INFO_ACK = 0x31,
    PORTAL_TYPE_USER_ONLINE = 0x32,
    PORTAL_TYPE_USER_OFFLINE = 0x34
};

enum portal_url_args_e {
    PORTAL_URL_USERIP,
    PORTAL_URL_ACNAME,
    PORTAL_URL_SSID,
    PORTAL_URL_USERMAC,
    PORTAL_URL_APMAC,
    PORTAL_URL_FIRSTURL,
    PORTAL_URL_WINTERFACE,
    PORTAL_URL_ACIP,
    PORTAL_URL_ENCRYPT,
    PORTAL_URL_USERNAME,
    PORTAL_URL_PASSWORD,
    PORTAL_URL_MAX
};

struct portal_pkt_s {
    uchar ver;
    uchar type;
    uchar pap_chap;
    uchar rsv;
    ushort serialno;
    ushort reqid;
    uint userip;
    ushort userport;
    uchar errcode;
    uchar attrnum;
    uchar attrs[0];
} __attribute__((packed));


struct tlv_s {
    uchar type;
    uchar len;
    uchar value[0];
};

struct portal_url_arg_map_s {
  char *arg_name;
  int arg_type;
};

struct portal_uri_store_s {
  int arg_type;
  char *start;
  int len;
};

struct portal_acip_s {
    struct list_head lst;
    char acname[64];
    IPADDRTYPE acip;
    char key[255];
    char md5[33];
};

struct portal_args_s {
  int httpd_fd;
  int epoll_fd;
  int event_fd;
  int portal_fd;
  int portal_send_fd;
  int bind_portal_fd;
  int http_port;
  int portal_port;
  int bind_portal_port;
  int portal_send_port;
  int portal_mac_sms_shuffle;
  IPADDRTYPE portal_ip;
  int authen_mode;
  char key[PORTAL_KEY_LEN];
  char portal_db[DATABASE_FILE];
  sqlite3 *portal_table;
  struct epoll_event epoll_events[MAX_EPOLL_EVENT];
  struct list_head hlst[FD_POLL_SIZE];
  struct list_head lst;
  struct list_head v4_user;
  struct list_head v6_user;
  struct list_head acip_lst;
  struct list_head bind_lst;
  int sock_num;
};

struct portal_http_header_s {
  char *buf;
  char method;
  char *uri;
  char *args;
  char *user_agent;
  char *content;
};

struct portal_fd_poll_s {
  struct list_head hlst;
  struct list_head lst;
  struct list_head usr_lst;
  int fd;
  int ts;
  int auth_req;
  struct portal_user_s *user;
};

struct portal_user_s {
  struct list_head lst;
  struct list_head sock_lst;
  int sock_num;
  int af;
  IPADDRTYPE ipv4;
  IPADDRTYPE6 ipv6;
  IPADDRTYPE nasip;
  int authen_mode;
  uchar mac[MAC_ADDR_LEN];
  uchar apmac[MAC_ADDR_LEN];
  int state;
  ushort req_id;
  ushort serialno;
  uchar challenge[CHALLENGE_LEN];
  bool bind_mac;
  int bind_info;
  char err_id[ERR_ID_LEN];
  char username[USERNAME_LEN];
  char password[PASSWORD_LEN];
  char ssid[SSID_LEN];
  char acname[ACNAME_LEN];
  char sessionid[SESSIONID_LEN * 2 + 1];
};

struct bind_user_s {
    struct list_head lst;
    uchar mac[6];
    char username[USERNAME_LEN];
    char password[PASSWORD_LEN];
};

extern bool portal_debug;
extern struct portal_args_s portal_args;

extern void portal_str_to_hex(char *string, int len, uchar *data);

extern void portal_hex_to_str(uchar *hex, int len, char *str);

#endif /* __PORTAL_H__ */
